/*
 * Galaxium Messenger
 * Copyright (C) 2005-2007 Ben Motmans <ben.motmans@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Text;
using System.Security.Cryptography;

namespace Galaxium.Protocol.Msn
{
	public abstract class AbstractChallenge : IChallenge
	{
		public abstract string GetChallengeResponse (string challenge);

		protected int[] CreateSmallChunks (string hash)
		{
			int[] smallChunks = new int[4];

			for (int i = 0; i < 4; i++)
			{
				smallChunks[i] = Convert.ToInt32 ("0x" + HexSwap (hash.Substring (0, 8)), 16);
				smallChunks[i] = smallChunks[i] & 0x7FFFFFFF;

				hash = hash.Remove (0, 8);
			}

			return smallChunks;
		}

		protected int[] CreateBigChunks (string challenge)
		{
			int[] bigChunks = new int[challenge.Length / 4];

			for (int i = 0; i < bigChunks.Length; i++)
			{
				string chunk = challenge.Substring (i * 4, 4);
				string hex = String.Empty;

				foreach (char c in chunk)
					hex = hex + String.Format ("{0:x}", (int)c);

				hex = HexSwap (hex);

				bigChunks[i] = Convert.ToInt32 ("0x" + hex, 16);
			}

			return bigChunks;
		}

		protected long CalculateKey (int[] bigChunks, int[] smallChunks)
		{
			long high = 0;
			long low = 0;

			for (int i = 0; i < bigChunks.Length; i = i + 2)
			{
				long tmp = bigChunks[i];
				tmp = (tmp * 242854337);
				tmp = tmp % 0x7FFFFFFF;
				tmp += high;
				tmp = smallChunks[0] * tmp + smallChunks[1];
				tmp = tmp % 0x7FFFFFFF;

				high = bigChunks[i + 1];
				high = (high + tmp) % 0x7FFFFFFF;
				high = smallChunks[2] * high + smallChunks[3];
				high = high % 0x7FFFFFFF;

				low = low + high + tmp;
			}

			high = (high + smallChunks[1]) % 0x7FFFFFFF;
			string hexHigh = HexSwap (String.Format ("{0:x}", high));
			high = Convert.ToInt64 ("0x" + hexHigh, 16);

			low = (low + smallChunks[3]) % 0x7FFFFFFF;

			string hexLow = HexSwap (
				String.Format ("{0:x}", low).PadLeft (8, '0')
			);

			low = Convert.ToInt64 ("0x" + hexLow, 16);

			long key = (high << 32) + low;

			return (key);
		}

		protected string CreateMD5HexString (string str)
		{
			MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider ();
			byte[] hash = md5.ComputeHash (Encoding.UTF8.GetBytes (str));

			StringBuilder sb = new StringBuilder ();

			foreach (byte b in hash)
				sb.Append (String.Format ("{0:x2}", b));

			return sb.ToString ();
		}

		protected string HexSwap (string str)
		{
			StringBuilder sb = new StringBuilder ();

			if (str.Length % 2 > 0)
				str = "0" + str;

			for (int i = 0; i < str.Length; i += 2)
				sb.Insert (0, str.Substring (i, 2));

			return (sb.ToString ());
		}
	}
}